package cn.anecansaitin.whimsy.common.block.entity;

import cn.anecansaitin.whimsy.common.capabilities.IManaStorage;
import cn.anecansaitin.whimsy.common.capabilities.RockScepterManaStorage;
import cn.anecansaitin.whimsy.common.register.ModBlockEntities;
import cn.anecansaitin.whimsy.common.register.ModParticles;
import cn.anecansaitin.whimsy.common.register.ModTags;
import net.minecraft.core.BlockPos;
import net.minecraft.core.NonNullList;
import net.minecraft.core.particles.ItemParticleOption;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.Containers;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
import net.neoforged.neoforge.items.ItemHandlerHelper;
import net.neoforged.neoforge.items.ItemStackHandler;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.List;

public class RockAltarEntity extends BlockEntity {
    public final ItemStackHandler itemHandler = new ItemStackHandler() {
        @Override
        protected void onContentsChanged(int slot) {
            setChanged();
        }
    };
    private int inspectTick = 0;
    private boolean diorite = false;
    private AABB detectionRange = new AABB(-4, -2, -4, 5, 3, 5);
    public static final List<Struct> DIORITE_STRUCT = new ArrayList<>();
    private float spin;

    static {
        DIORITE_STRUCT.add(new Struct(Blocks.POLISHED_DIORITE, 0, -1, 0));
        DIORITE_STRUCT.add(new Struct(Blocks.DIORITE_STAIRS, -1, -2, -1));
        DIORITE_STRUCT.add(new Struct(Blocks.DIORITE_STAIRS, -1, -2, 1));
        DIORITE_STRUCT.add(new Struct(Blocks.DIORITE_STAIRS, 1, -2, -1));
        DIORITE_STRUCT.add(new Struct(Blocks.DIORITE_STAIRS, 1, -2, 1));
        DIORITE_STRUCT.add(new Struct(Blocks.POLISHED_DIORITE_STAIRS, 1, -2, 0));
        DIORITE_STRUCT.add(new Struct(Blocks.POLISHED_DIORITE_STAIRS, -1, -2, 0));
        DIORITE_STRUCT.add(new Struct(Blocks.POLISHED_DIORITE_STAIRS, 0, -2, 1));
        DIORITE_STRUCT.add(new Struct(Blocks.POLISHED_DIORITE_STAIRS, 0, -2, -1));
        DIORITE_STRUCT.add(new Struct(Blocks.AMETHYST_BLOCK, 0, -2, 0));
        DIORITE_STRUCT.add(new Struct(Blocks.DIORITE_WALL, -2, -2, -2));
        DIORITE_STRUCT.add(new Struct(Blocks.DIORITE_WALL, -2, -2, 2));
        DIORITE_STRUCT.add(new Struct(Blocks.DIORITE_WALL, 2, -2, -2));
        DIORITE_STRUCT.add(new Struct(Blocks.DIORITE_WALL, 2, -2, 2));
        DIORITE_STRUCT.add(new Struct(Blocks.REDSTONE_LAMP, -2, -3, -2));
        DIORITE_STRUCT.add(new Struct(Blocks.REDSTONE_LAMP, -2, -3, 2));
        DIORITE_STRUCT.add(new Struct(Blocks.REDSTONE_LAMP, 2, -3, -2));
        DIORITE_STRUCT.add(new Struct(Blocks.REDSTONE_LAMP, 2, -3, 2));
    }

    public RockAltarEntity(BlockPos pos, BlockState blockState) {
        super(ModBlockEntities.ROCK_ALTAR.get(), pos, blockState);
        detectionRange = detectionRange.move(pos);
        setChanged();
    }

    public void tick(Level level, BlockPos pos, BlockState state) {
        inspectMultiBlock(level, pos);
        IManaStorage manaStorage = itemHandler.getStackInSlot(0).getCapability(IManaStorage.MANA_STORAGE);

        if (diorite && manaStorage != null) {
            if (manaStorage.canReceive(RockScepterManaStorage.DIORITE_TYPE)) {
                List<ItemEntity> itemEntities = level.getEntitiesOfClass(ItemEntity.class, detectionRange);

                for (ItemEntity itemEntity : itemEntities) {
                    ItemStack itemStack = itemEntity.getItem();

                    if (!itemStack.is(ModTags.DIORITE)) {
                        continue;
                    }

                    int scale;

                    if (itemStack.is(Items.DIORITE) || itemStack.is(Items.POLISHED_DIORITE) || itemStack.is(Items.DIORITE_WALL)) {
                        scale = 20;
                    } else if (itemStack.is(Items.DIORITE_SLAB) || itemStack.is(Items.POLISHED_DIORITE_SLAB)) {
                        scale = 10;
                    } else if (itemStack.is(Items.DIORITE_STAIRS) || itemStack.is(Items.POLISHED_DIORITE_STAIRS)) {
                        scale = 13;
                    } else {
                        scale = 8;
                    }

                    manaStorage.receiveMana(RockScepterManaStorage.DIORITE_TYPE, scale, false);

                    if (getLevel().isClientSide()) {
                        getLevel().addParticle(new ItemParticleOption(ModParticles.NO_PHYSICAL_ITEM_PARTICLE.get(), itemStack), itemEntity.getX(), itemEntity.getY() + 0.5, itemEntity.getZ(), pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5);
                    }

                    itemStack.setCount(itemStack.getCount() - 1);
                    break;
                }
            }
        }

        inspectTick = inspectTick >= 20 ? 0 : inspectTick + 1;
    }

    @Override
    protected void saveAdditional(CompoundTag tag) {
        super.saveAdditional(tag);
        tag.put("inventory", itemHandler.serializeNBT());
    }

    @Override
    public void load(CompoundTag tag) {
        super.load(tag);
        itemHandler.deserializeNBT(tag.getCompound("inventory"));
    }

    @Override
    public CompoundTag getUpdateTag() {
        CompoundTag tag = new CompoundTag();
        tag.put("inventory", itemHandler.serializeNBT());
        return tag;
    }

    @Override
    public void handleUpdateTag(CompoundTag tag) {
        super.handleUpdateTag(tag);
        itemHandler.deserializeNBT(tag.getCompound("inventory"));
    }

    @Nullable
    @Override
    public Packet<ClientGamePacketListener> getUpdatePacket() {
        return ClientboundBlockEntityDataPacket.create(this);
    }

    @Override
    public void setChanged() {
        super.setChanged();

        if (level instanceof ServerLevel) {
            level.sendBlockUpdated(worldPosition, getBlockState(), getBlockState(), Block.UPDATE_CLIENTS);
        }
    }

    public InteractionResult exchangeItem(Player player, InteractionHand hand) {
        ItemStack itemStack = player.getItemInHand(hand);
        ItemStack stackInSlot = itemHandler.getStackInSlot(0);

        if (stackInSlot.isEmpty()) {
            if (itemStack.isEmpty()) {
                return InteractionResult.PASS;
            } else {
                itemHandler.setStackInSlot(0, itemStack);
                player.setItemInHand(hand, ItemStack.EMPTY);
                return InteractionResult.SUCCESS;
            }
        } else {
            ItemHandlerHelper.giveItemToPlayer(player, stackInSlot, player.getInventory().selected);
            itemHandler.setStackInSlot(0, ItemStack.EMPTY);
            return InteractionResult.SUCCESS;
        }
    }

    public void dropItem() {
        NonNullList<ItemStack> itemStacks = NonNullList.of(ItemStack.EMPTY, itemHandler.getStackInSlot(0));
        Containers.dropContents(getLevel(), getBlockPos(), itemStacks);
    }

    private void inspectMultiBlock(Level level, BlockPos pos) {
        if (inspectTick == 20) {
            diorite = inspectDiorite(level, pos);
        }
    }

    private boolean inspectDiorite(Level level, BlockPos pos) {
        for (Struct struct : DIORITE_STRUCT) {
            if (!inspectBlock(level, pos, struct)) {
                return false;
            }
        }

        return true;
    }

    private boolean inspectBlock(Level level, BlockPos pos, Struct struct) {
        return level.getBlockState(pos.offset(struct.xOffset, struct.yOffset, struct.zOffset)).is(struct.block);
    }

    public float getSpin() {
        if (spin >= 360) {
            spin = 0.02f;
        } else {
            spin += 0.02f;
        }

        return spin;
    }

    public record Struct(Block block, int xOffset, int yOffset, int zOffset) {
    }
}
